/*
 * This file is part of the Android Dimensions Editor Project: http://code.google.com/p/android-dimension-editor/
 *  
 * Copyright (c) 2013 Rick Falck
 *  
 * This code is licensed under the terms of the Apache License Version 2.0.
 * You may use this code according to the license.
 *
 * The terms of the license can be found in the root directory of this project's repository as well as at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under these Licenses is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See each License for the specific language governing permissions and
 * limitations under that License.
 */
package model;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import model.ReturnInfo.ERROR_TYPE;

/**
 * <b>public class DimensGrid</b>
 * <p>
 * This class manages the Dimension Files (DimensFile)
 *
 * @author Rick Falck
 */
public class DimensGrid {

    public static String DIMENS_FILENAME = null;
    public static String DIMENS_FOLDER_PREFIX = "values";
    public static String DIMENS_FOLDER_BACKUP = "DimensFilesBackups";

    private final File mProjectResPath;

    private ArrayList<DimensFile> mFiles = new ArrayList<DimensFile>();

    private ArrayList<ItemName> mItemNameList = new ArrayList<ItemName>();

    public DimensGrid(File projectResPath) {
        mProjectResPath = projectResPath;
    }

    public ReturnInfo readFiles(Map<String, File> valuesFiles) {
        ReturnInfo ret = null;
        // Create the files objects
        for (String folder : valuesFiles.keySet()) {
            File f = valuesFiles.get(folder);

            DimensFile df = new DimensFile(folder, f);
            ret = df.readFile();
            if (!ret.isSuccess()) {
                return ret;
            }
            mFiles.add(df);
        }

        // For all files, get all item names
        for (DimensFile file : mFiles) {
            List<ItemName> fileNameItems = file.getItemNames();
            for (ItemName name : fileNameItems) {
                if (!mItemNameList.contains(name)) {
                    if (name.isComment()) {
                        mItemNameList.add(new ItemComment(name.getText()));
                    } else {
                        mItemNameList.add(new ItemName(name.getText()));
                    }
                }
            }
        }

        // If a file is missing any items that are in other file(s),
        // add a new item to the file with the default value
        for (ItemName name : mItemNameList) {
            for (DimensFile file : mFiles) {
                file.addIfMissing(name);
            }
        }

        return new ReturnInfo(true);
    }

    public ReturnInfo saveAll() {
        ReturnInfo retInfo;
        for (DimensFile df : mFiles) {
            retInfo = df.editValues();
            if (!retInfo.isSuccess()) {
                return retInfo;
            }
        }

        for (ItemName item : mItemNameList) {
            if (item.isNameChanged()) {
                for (DimensFile df : mFiles) {
                    df.changeItemName(item.getOriginalString(), item.getText());
                }
                item.setToCurrentName();
            }
        }

        for (DimensFile df : mFiles) {
            retInfo = df.save(); // does nothing if the file does not need
            // saving
            if (!retInfo.isSuccess()) { // Exception
                return retInfo;
            }
        }

        return new ReturnInfo(true); // success!
    }

    // public void addNewItemName(String itemName, String itemValue) {
    // for (DimensFile df : mFiles) {
    // df.addNewItem(itemName, itemValue);
    // }
    // }
    public void addNewFile(String folderName) {
        mFiles.add(DimensFile.createNewFile(folderName, new File(mProjectResPath + File.separator
                + folderName, DIMENS_FILENAME), mItemNameList));
    }

    public void deleteFile(String folderName) {
        DimensFile file = null;
        int index = 0;
        for (DimensFile df : mFiles) {
            if (df.getFolderName().equalsIgnoreCase(folderName)) {
                file = df;
                break;
            }
            index++;
        }

        if (file != null) {
            mFiles.remove(index);
            file.delete();
        }
    }

    public ArrayList<DimensFile> getAllFiles() {
        return mFiles;
    }

    public DimensFile getFile(int index) {
        if (index < mFiles.size()) {
            return mFiles.get(index);
        }
        return null;
    }

    public DimensFile getFile(String folderName) {
        for (DimensFile file : mFiles) {
            if (file.getFolderName().equals(folderName)) {
                return file;
            }
        }
        return null;
    }

    public List<ItemName> getItemNameList() {
        return mItemNameList;
    }

    public int getItemNameIndex(ItemName iName) {
        int index = 0;
        for (ItemName item : mItemNameList) {
            if (item.equals(iName)) {
                return index;
            }
            index++;
        }
        return -1;
    }

    public ArrayList<DimensFile> getFiles() {
        return mFiles;
    }

    public ArrayList<String> getFolderNames() {
        ArrayList<String> fNames = new ArrayList<>();
        for (DimensFile file : mFiles) {
            fNames.add(file.getFolderName());

        }
        return fNames;
    }

    public void deleteItemName(ItemName itemName) {
        mItemNameList.remove(itemName);
        for (DimensFile file : mFiles) {
            file.deleteItemName(itemName);
        }
    }

    public boolean folderExists(String folderName) {
        for (DimensFile file : mFiles) {
            if (file.getFolderName().equalsIgnoreCase(folderName)) {
                return true;
            }
        }
        return false;
    }

    public boolean NameItemExists(String nameItem) {
        return mItemNameList.contains(nameItem);
    }

    public void addNewNameItem(ItemName currentItem, String newItemName, boolean insertAbove) {
        ItemName nItem = new ItemName(newItemName);
        if (currentItem == null) {
            mItemNameList.add(nItem);
            return;
        }

        int pos = mItemNameList.indexOf(currentItem);
        if (pos == -1) {
            return;
        }
        if (insertAbove) {
            mItemNameList.add(pos, nItem);
        } else {
            if (pos == mItemNameList.size() - 1) {
                mItemNameList.add(nItem);
            } else {
                mItemNameList.add(pos + 1, nItem);
            }
        }

        for (DimensFile file : mFiles) {
            file.addNewItem(currentItem, newItemName, insertAbove);
        }
    }

    public void addNewCommentItem(ItemName currentItem, String comment) {
        ItemComment nItem = new ItemComment(comment);
        int pos = mItemNameList.indexOf(currentItem);
        if (pos == -1) {
            return;
        }
        mItemNameList.add(pos, nItem);
        for (DimensFile file : mFiles) {
            file.addNewComment(currentItem, comment);
        }
    }

    public int getLongestNameLength() {
        int biggest = 0;
        for (ItemName item : mItemNameList) {
            if (item.getText().length() > biggest) {
                biggest = item.getText().length();
            }
        }
        return biggest;
    }

    public void sort() {
        Collections.sort(mItemNameList);
        for (DimensFile file : mFiles) {
            file.sort();
        }
    }

    public void moveItemDown(ItemName selectedItem) {
        int pos = mItemNameList.indexOf(selectedItem);
        if (pos == -1 || pos == mItemNameList.size() - 1) {
            return;
        }

        ItemName name = mItemNameList.remove(pos);
        mItemNameList.add(pos + 1, name);
    }

    public void moveItemUp(ItemName selectedItem) {
        int pos = mItemNameList.indexOf(selectedItem);
        if (pos == -1 || pos == 0) {
            return;
        }

        ItemName name = mItemNameList.remove(pos);
        mItemNameList.add(pos - 1, name);
    }

    public ReturnInfo backup(String path, Map<String, File> dimensFiles) {
        Date today = new Date();
        String dirBackup = DIMENS_FOLDER_BACKUP + today.toString();
        dirBackup = dirBackup.replace(":", "_");
        File projBackupDir = new File(path, dirBackup);
        
        if (projBackupDir.exists()) {
            return new ReturnInfo(ERROR_TYPE.FILE_IO, "Backup directory already exists");
        }
        projBackupDir.mkdirs();

        for (File fromFile : dimensFiles.values()) {
            String vDir = FilenameUtils.getBaseName(fromFile.getParent());
            File toFilePath = new File(projBackupDir, vDir);
            if (!toFilePath.exists()) {
                toFilePath.mkdir();
            }
            File toFile = new File(toFilePath, DIMENS_FILENAME);
            try {
                copyFiles(fromFile, toFile);
            } catch (IOException e) {
                return new ReturnInfo(ERROR_TYPE.FILE_IO, "Failed to backup files");
            }
        }
        return new ReturnInfo(true);
    }

    private void copyFiles(File fromFile, File toFile) throws IOException {

        FileChannel fromChannel = null;
        FileChannel toChannel = null;
        FileInputStream inStream = null;
        FileOutputStream outStream = null;

        try {
            inStream = new FileInputStream(fromFile);
            fromChannel = inStream.getChannel();
            outStream = new FileOutputStream(toFile);
            toChannel = outStream.getChannel();
            toChannel.transferFrom(fromChannel, 0, fromChannel.size());
            inStream.close();
            outStream.close();
            fromChannel.close();
            toChannel.close();
        } finally {
            if (fromChannel != null) {
                try {
                    fromChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    throw e;
                }
            }
            if (toChannel != null) {
                try {
                    toChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    throw e;
                }
            }
        }

    }
}
